let objState;

export default function rainbowCursor(options) {
    let hasWrapperEl = options && options.element;
    let element = hasWrapperEl || document.body;
    let width = window.innerWidth;
    let height = window.innerHeight;
    let cursor = {x: width / 2, y: width / 2};
    let particles = [];
    let canvas, context, animationFrame ;

    const totalParticles = options?.length || 20;
    const colors = options?.colors || [
        "#FE0000",
        "#FD8C00",
        "#FFE500",
        "#119F0B",
        "#0644B3",
        "#C22EDC",
    ];
    const size = options?.size || 3;

    let cursorsInitted = false;
    objState = {
        cursor: cursor,
        particles: particles,
        element: element,
        canvas: canvas,
        context: context,
        animationFrame: animationFrame,
        totalParticles: totalParticles,
        colors: colors,
        size: size,
        hasWrapperEl: hasWrapperEl,
        width: width,
        height: height,
        cursorsInitted: cursorsInitted
    };

    init();

    return {
        destroy: destroy
    }
}

function init() {
    try {
        objState.canvas = document.createElement("canvas");
        objState.context = objState.canvas.getContext("2d");
        objState.canvas.style.top = "0px";
        objState.canvas.style.left = "0px";
        objState.canvas.style.pointerEvents = "none";

        if (objState.hasWrapperEl) {
            objState.canvas.style.position = "absolute";
            objState.element.appendChild(objState.canvas);
            objState.canvas.width = objState.element.clientWidth;
            objState.canvas.height = objState.element.clientHeight;
        } else {
            objState.canvas.style.position = "fixed";
            document.body.appendChild(objState.canvas);
            objState.canvas.width = objState.width;
            objState.canvas.height = objState.height;
        }

        bindEvents(objState.element);
        loop();
    } catch (error) {
        console.error("init rainbow cursor error", error);
    }
}

function bindEvents(element) {
    element.addEventListener("mousemove", onMouseMove);
    window.addEventListener("resize", onWindowResize);
}

function onWindowResize() {
    objState.width = window.innerWidth;
    objState.height = window.innerHeight;

    if (objState.hasWrapperEl) {
        objState.width = objState.element.clientWidth;
        objState.canvas.height = objState.element.clientHeight;
    } else {
        objState.canvas.width = objState.width;
        objState.canvas.height = objState.height;
    }
}

function onMouseMove(e) {
    if (objState.hasWrapperEl) {
        const boundingRect = objState.element.getBoundingClientRect();
        objState.cursor.x = e.clientX - boundingRect.left;
        objState.cursor.y = e.clientY - boundingRect.top;
    } else {
        objState.cursor.x = e.clientX;
        objState.cursor.y = e.clientY;
    }

    if (objState.cursorsInitted === false) {
        objState.cursorsInitted = true;
        for (let i = 0; i < objState.totalParticles; i++) {
            addParticle(objState.cursor.x, objState.cursor.y);
        }
    }
}

function addParticle(x, y, image) {
    objState.particles.push(new Particle(x, y, image));
}

function updateParticles() {
    objState.context.clearRect(0, 0, objState.width, objState.height);
    objState.context.lineJoin = "round";

    let particleSets = [];

    let x = objState.cursor.x;
    let y = objState.cursor.y;

    objState.particles.forEach(function (particle, index, particles) {
        let nextParticle = particles[index + 1] || particles[0];

        particle.position.x = x;
        particle.position.y = y;

        particleSets.push({x: x, y: y});

        x += (nextParticle.position.x - particle.position.x) * 0.4;
        y += (nextParticle.position.y - particle.position.y) * 0.4;
    });

    objState.colors.forEach((color, index) => {
        objState.context.beginPath();
        objState.context.strokeStyle = color;

        if (particleSets.length) {
            objState.context.moveTo(
                particleSets[0].x,
                particleSets[0].y + index * (objState.size - 1)
            );
        }

        particleSets.forEach((set, particleIndex) => {
            if (particleIndex !== 0) {
                objState.context.lineTo(set.x, set.y + index * objState.size);
            }
        });

        objState.context.lineWidth = objState.size;
        objState.context.lineCap = "round";
        objState.context.stroke();
    });
}

function loop() {
    updateParticles();
    objState.animationFrame = requestAnimationFrame(loop);
}

function destroy() {
    objState.canvas.remove();
    cancelAnimationFrame(objState.animationFrame);
    objState.element.removeEventListener("mousemove", onMouseMove);
    window.addEventListener("resize", onWindowResize);
}

function Particle(x, y) {
    this.position = {x: x, y: y};
}
